<?php

/**
 * Объект
 */
class Object implements iObject {
	
	private
		$id = 0,
		$typeId = 0,
		$properties         = array (),
		$updatedProperties  = array (),
		$aAttributes        = array (),
		$aDisabledHTMLFilter = array (),
		$aDisabledSimpleHTMLFilter = array();
	
	/**
	 * Конструктор
	 * @param Type $oType тип объекта
	 * @param integer $id
	 */
	public function __construct (Type $oType, $id = 0) {
		$this->typeId = $oType->getId();
		$aFields = $oType->getFieldsNames ();
		
		foreach ($aFields as $field) {
			$this->updatedProperties[$field] = false;
		}
		
		$this->id = (int) $id;		
	}
	
	public static function __set_state(array $values) {
		$object = new Object(TypesController::getInstance()->getTypeById($values['typeId']), $values['id']);
		$object->setValues($values['properties']);
		
		return $object;
	}
	
	public function __sleep() {
		return array('typeId', 'id', 'properties');
	}
	
	/**
	 * Получить id объекта
	 * @return integer
	 */
	public function getId () {
		return $this->id;
	}
	
	/**
	 * Установить id объекта
	 * @param integer $id
	 * @return void
	 */
	public function setId ($id) {
		$this->id = (int) $id;
	}
	
	/**
	 * Сохранить измененные значения полей объекта
	 * @return boolean
	 */
	public function save () {
		return ObjectsController::getInstance()->saveObject ($this);
	}
	
	/**
	 * Удалить объект
	 * @return boolean
	 */
	public function delete () {
		return ObjectsController::getInstance()->deleteObject ($this);
	}
	
	/**
	 * Создать новую запись об объекте в БД
	 * @return boolean
	 */
	public function createNew () {
		return ObjectsController::getInstance()->createNewObject ($this);
	}
	
	/**
	 * Получить тип объекта
	 * @return Type
	 */
	public function getType () {
		//return $this->oType;
		return TypesController::getInstance()->getTypeById($this->typeId);
	}
	
	/**
	 * Получить имя типа
	 * @return string
	 */
	public function getTypeName () {
		//return $this->oType->getName ();
		return $this->getType()->getName();
	}
	
	/**
	 * Получить массив свойств объекта
	 * @return array массив, где ключ - название поля
	 */
	public function getValues () {
		return $this->properties;
	}
	
	/**
	 * Получить значение свойства
	 * @param string $fieldName имя поля
	 * @retutn mixed
	 */
	public function getValue ($fieldName) {
		return isset ($this->properties[$fieldName]) ? $this->properties[$fieldName] : false;
	}
	
	/**
	 * Установить значение свойства
	 * @param string $fieldName имя поля
	 * @param mixed $value значение
	 * @param boolean $setUpdated отметить свойство как обновленное
	 * @return void
	 */
	public function setValue ($fieldName, $value, $setUpdated = true) {
		$this->updatedProperties[$fieldName] = (bool) $setUpdated;
		
		//$aFields = $this->oType->getFieldsNames ();
		$aFields = $this->getType()->getFieldsNames ();
		
		if (in_array ($fieldName, $aFields)) {
			$this->properties[$fieldName] = $value;
		}
	}
	
	/**
	 * Установить значения нескольких свойств
	 * @param array $properties
	 * @param boolean $setUpdated
	 * @return void
	 */
	public function setValues (array $properties, $setUpdated = true) {
		//$aFields = $this->oType->getFieldsNames ();
		$aFields = $this->getType()->getFieldsNames ();
		
		foreach ($properties as $fieldName => $propValue) {
			
			if (in_array ($fieldName, $aFields)) {
				$this->updatedProperties[$fieldName] = (bool) $setUpdated;

				$this->properties[$fieldName] = $propValue;
			}
		}
	}
	
	/**
	 * Получить состояние объекта или его поля (обновлен/нет)
	 * @param string $fieldName название поля; если этот параметр передан, то проверяется состояние поля
	 * @return boolean
	 */
	public function isUpdated ($fieldName = false) {
		if ($fieldName) {
			if (isset ($this->updatedProperties[$fieldName]) && $this->updatedProperties[$fieldName] === true) {
				return true;
			} else {
				return false;
			}
		} else {
			return in_array (true, $this->updatedProperties) ? true : false;
		}
	}
	
	/**
	 * Установить состояние объекта (обновлен/нет)
	 * @param boolean $status значение true указывает на то, что объект обновлен
	 * @return void
	 */
	public function setUpdated ($status = true) {
		foreach ($this->updatedProperties as $fieldName => $j) {
			$this->updatedProperties[$fieldName] = (bool) $status;
		}
	}
	
	/**
	 * Получить объект в виде XML-таблицы
	 * @param DOMDocument $xml
	 * @return DOMElement
	 */
	public function __toXML (DOMDocument $xml) {
		$oType = $this->getType ();
		$sTypeName = $oType->getName ();
		$aValues = $this->getValues ();
		$aFields = $oType->getFields ();
		
		$objectNode = $xml->createElement ('object');
		$objectNode->setAttribute ('id', $this->getId());
		$objectNode->setAttribute ('type', $sTypeName);
		$objectNode->setAttribute ('type-id', $oType->getId());
		
		// Атрибуты
		$aAttributes = $this->getAttributes ();
		foreach ($aAttributes as $atrName => $atrValue) {
			$objectNode->setAttribute ($atrName, $atrValue);
		}
		
		foreach ($aFields as $fieldName => $oField) {
			if (isset ($aValues[$fieldName])) {
				$value = $aValues[$fieldName];
			} else {			
				$character = $oField->getCharacter ();
				
				if ($character == 'guide' || $character == 'multiguide' || $character == 'tags') {
					$value = array ();
				} elseif ($character == 'date' || $character == 'birthdate') {
					$value = 0;
				} else {
					$value = '';
				}
			}						
			
			$objectNode->appendChild ($this->renderField ($xml, $value, $oField));
		}
		
		return $objectNode;
	}
	
	/**
	 * Установить атрибут
	 * @param string $name имя
	 * @param mixed $value значение
	 */
	public function setAttribute ($name, $value) {
		if ($name != '') {
			$this->aAttributes[$name] = $value;
		}
	}
	
	/**
	 * Получить значение атрибута
	 * @param string $name
	 * @return mixed
	 */
	public function getAttribute($name) {
		return isset($this->aAttributes[$name]) ? $this->aAttributes[$name] : false;
	}
	
	/**
	 * Получить массив значений атрибутов
	 * @return array
	 */
	public function getAttributes () {
		return $this->aAttributes;
	}
	
	/**
	 * Отключить HTML-фильтр (Jevix) у поля
	 * @param string $fieldName
	 * @return void
	 */
	public function disableHTMLFilter ($fieldName, $lite = true) {
		if ($lite) {
			$this->aDisabledSimpleHTMLFilter[] = $fieldName;
		} else {
			$this->aDisabledHTMLFilter[] = $fieldName;
		}
	}
	
	/**
	 * Обрабатывает поле
	 * @return DOMElement
	 */
	private function renderField (DOMDocument $xml, $value, Field $oField) {
		$fieldNode = $xml->createElement ('property');
		$fieldNode->setAttribute ('field-id', $oField->getId ());
		$fieldNode->setAttribute ('name', $oField->getName ());
				
		$character = $oField->getCharacter ();
		
		if ($oField->getName () == 'password') {
			$value = '';
		}
		
		if ($character == 'text') {
			if (!in_array ($oField->getName(), $this->aDisabledHTMLFilter)) {
				$value = Jevix::parse ($value, in_array ($oField->getName(), $this->aDisabledSimpleHTMLFilter) ? false : true);
			}
		}
		
		$value = str_replace ('&', '&amp;', $value);
		
		$fieldNode->appendChild ($xml->createElement ('title', $oField->getTitle ()));
		
		if (($character == 'guide' || $character == 'multiguide') && is_array ($value)) {
			$fieldNode->setAttribute ('character', $character);
			
			$fieldNode->setAttribute ('guide-id', $oField->getAddId ());
			
			foreach ($value as $objectId) {
				$fieldNode->appendChild ($xml->createElement ('value', $objectId));
			}
		}
		elseif ($character == 'tags' && is_array ($value)) {
			$fieldNode->setAttribute ('character', $character);
			foreach ($value as $tag) {
				$tag = str_replace ('&', '&amp;', $tag);
				$fieldNode->appendChild ($xml->createElement ('value', $tag));
			}
		}
		else {		
			// Если поле чужое, то ставим характер чужого поля
			if (!$oField->isReal ()) {
				$oConnectedField = FieldsController::getInstance()->getField ($oField->getAddId ());
				$fieldNode->setAttribute ('character', $oConnectedField->getCharacter ());
			} else {
				$fieldNode->setAttribute ('character', $oField->getCharacter ());
			}
			
			if ($character == 'date') {
				$value = formatDate ($value);
			}
			
			if ($character == 'birthdate') {
				$value = formatDate ($value, true);
			}
			
			$fieldNode->appendChild ($xml->createElement ('value', (string) $value));
		}
		
		return $fieldNode;
	}

}